package com.boot2.core.conf.swagger;

import com.boot2.core.HlAssert;
import com.boot2.core.SysProperties;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.AntPathMatcher;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhangweilin
 * @description: xxxxx
 * @date 2018/10/31
 */
//@ConfigurationProperties(prefix = "sys.swagger")
public abstract class BaseSwagger2Config {

    @Autowired
    SysProperties sysProperties;

    @Autowired
    private AntPathMatcher springAntMatcher;

    @Autowired
    Environment environment;

    @Bean
    public Docket userApi() {
        SysProperties.Swagger2Properties swagger = sysProperties.getSwagger();
        return getDocket(environment.getProperty("spring.application.name"), swagger.getBasePackages());
    }

    protected List<ApiKey> securitySchemes() {
        SysProperties.Swagger2Properties swagger = sysProperties.getSwagger();
        String tokenKey = swagger.getTokenKey();
        List<ApiKey> apiKeyList = new ArrayList<>(1);
        ApiKey apiKey = new ApiKey(tokenKey, tokenKey, "header");
        apiKeyList.add(apiKey);
        return apiKeyList;
    }

    protected List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.any()).build());
        return securityContexts;
    }

    private List<SecurityReference> defaultAuth() {
        SysProperties.Swagger2Properties swagger = sysProperties.getSwagger();
        String tokenKey = swagger.getTokenKey();

        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        //此处tokenKey要和ApiKey中一致，否则不会做为header传到后台
        securityReferences.add(new SecurityReference(tokenKey, authorizationScopes));
        return securityReferences;
    }


    protected Docket getDocket(final String groupName, final List<String> basePackageList) {
        HlAssert.notEmpty(basePackageList,"请先配置swagger文档basePackage,支持ant风格路径，如sys.swagger.base-packages=com.bomi.**.controller,com.bomi.oper.controller,com.bomi.common.controller");
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        //声明swagger要以此域名为根路径,否则二级域名通过nginx转发后，还会以localhost为根路径导致404
//        docket = docket.host("http://api.v1.iyoju.com");
        docket.apiInfo(apiInfo(groupName)).forCodeGeneration(true).select()
                .apis(basePackage(basePackageList.toArray(new String[]{}))).paths(PathSelectors.any())
                .build().groupName(groupName).securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
        //要加上.globalOperationParameters(pars);才会在全局显示
        return docket;
    }

    /**
     *
     * @param groupName
     * @param basePackage  支持配置多个包，每个包支持ant风格表达式
     * @return
     */
    protected Docket getDocket(final String groupName, final String... basePackage) {
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        //声明swagger要以此域名为根路径,否则二级域名通过nginx转发后，还会以localhost为根路径导致404
//        docket = docket.host("http://api.v1.iyoju.com");
        docket.apiInfo(apiInfo(groupName)).forCodeGeneration(true).select()
                .apis(basePackage(basePackage)).paths(PathSelectors.any())
                .build().groupName(groupName).securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
        //要加上.globalOperationParameters(pars);才会在全局显示
        return docket;
    }

    protected abstract ApiInfo apiInfo(String groupName);


    /**
     * 重写basePackage方法，使能够实现多包访问，
     *
     * @param [basePackage]
     * @return com.google.common.base.Predicate<springfox.documentation.RequestHandler>
     * @author teavamc
     * @date 2019/1/26
     */
    protected Predicate<RequestHandler> basePackage(final String... basePackage) {
        return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
    }

    protected Function<Class<?>, Boolean> handlerPackage(final String... basePackage) {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackage) {
                String basePackageName = input.getPackage().getName();
//                boolean isMatch = basePackageName.startsWith(strPackage);
                boolean isMatch = springAntMatcher.match(strPackage, basePackageName);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    protected Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }
}

